#include "Packet.hpp"
#include "Log.hpp"
#include "MiscUtil.hpp"
#include "NetUtil.hpp"
#include "ExceptionUtil.hpp"
#include "KieperException.hpp"

#include <algorithm>

namespace kieper {
    using std::copy;
    Packet::~Packet() {}

    Packet::Packet(unsigned int payloadSize, Type type, Delivery delivery, Scope scope,
        Direction direction, uint16_t messageNumber) : 
        payloadSize_(payloadSize), buffer_(new char[HEADER_SIZE + payloadSize]) {
        setMessageInfo(type, delivery, scope, direction, messageNumber);
    }

    Packet::Packet(unsigned int payloadSize, const char* buffer) : 
        payloadSize_(payloadSize), buffer_(new char[HEADER_SIZE + payloadSize]) {
        copy(buffer, buffer + getTotalSize(), buffer_.get());
    }

    const char* Packet::getBuffer() const {
        return buffer_.get();
    }

    char* Packet::getPayload() const {
        return buffer_.get() + HEADER_SIZE;
    }

    unsigned int Packet::getTotalSize() const {
        return HEADER_SIZE + payloadSize_;
    }

    void Packet::setPacketId(uint32_t packetId) {
        netutil::setAsNbo32(buffer_.get(), 0, packetId);
    }
    
    void Packet::setDeviceId(uint16_t deviceId) {
        netutil::setAsNbo16(buffer_.get(), 2, deviceId);
    }

    void Packet::setMessageInfo(Packet::Type type, Packet::Delivery delivery,
        Packet::Scope scope, Packet::Direction direction, uint16_t messageNumber) {
        if (messageNumber > 4095) {
            THROW(KieperException, "Message number cannot be greater then 4095");
        }

        uint16_t messageInfo = 0;
        miscutil::setBitValue16(messageInfo, 15, type);
        miscutil::setBitValue16(messageInfo, 14, delivery);
        miscutil::setBitValue16(messageInfo, 13, scope);
        miscutil::setBitValue16(messageInfo, 12, direction);
        messageInfo |= messageNumber;

        netutil::setAsNbo16(buffer_.get(), 3, messageInfo);
    }

    uint32_t Packet::getPacketId() {
        return netutil::getFromNbo32(getBuffer(), 0);
    }

    uint16_t Packet::getDeviceId() {
        return netutil::getFromNbo16(getBuffer(), 2);
    }

    Packet::Type Packet::getType() {
        uint16_t messageInfo = netutil::getFromNbo16(getBuffer(), 3);
        if (!miscutil::getBitValue16(messageInfo, 15)) {
            return TYPE_CONTROL;
        } else {
            return TYPE_DATA;
        }
    }

    Packet::Delivery Packet::getDelivery() {
        uint16_t messageInfo = netutil::getFromNbo16(getBuffer(), 3);
        if (!miscutil::getBitValue16(messageInfo, 14)) {
            return DELIVERY_SYNC;
        } else {
            return DELIVERY_ASYNC;
        }
    }

    Packet::Scope Packet::getScope() {
        uint16_t messageInfo = netutil::getFromNbo16(getBuffer(), 3);
        if (!miscutil::getBitValue16(messageInfo, 15)) {
            return SCOPE_GLOBAL;
        } else {
            return SCOPE_DEVICE_SPECIFIC;
        }
    }

    Packet::Direction Packet::getDirection() {
        uint16_t messageInfo = netutil::getFromNbo16(getBuffer(), 3);
        if (!miscutil::getBitValue16(messageInfo, 15)) {
            return DIRECTION_REQUEST;
        } else {
            return DIRECTION_RESPONSE;
        }
    }

    uint16_t Packet::getMessageNumber() {
        uint16_t messageInfo = netutil::getFromNbo16(getBuffer(), 3);
        return messageInfo & 0x0FFF;
    }
}
